﻿#region Usings

using System;
using System.Collections.Generic;
using System.Windows;
using System.Windows.Interactivity;
using System.Windows.Media;
using SilverBox.Physics;

#endregion

namespace SilverBox.Behaviors
{
    public class Magnet : Behavior<FrameworkElement>
    {
        public static readonly DependencyProperty FallOffProperty = DependencyProperty.Register("FallOff",
                                                                                                typeof (double),
                                                                                                typeof (Magnet),
                                                                                                new PropertyMetadata(1.2));

        public static readonly DependencyProperty MagnetismProperty = DependencyProperty.Register("Magnetism",
                                                                                                  typeof (double),
                                                                                                  typeof (Magnet),
                                                                                                  new PropertyMetadata(
                                                                                                      .1));

        public static readonly DependencyProperty MaxDistanceProperty = DependencyProperty.Register("MaxDistance",
                                                                                                    typeof (double),
                                                                                                    typeof (Magnet),
                                                                                                    new PropertyMetadata
                                                                                                        (2.5));

        private static List<Magnet> worldMagnets = new List<Magnet>();
        private Entity entity;


        public Magnet()
        {
        }

        public double Magnetism
        {
            get { return (double) GetValue(MagnetismProperty); }
            set { SetValue(MagnetismProperty, value); }
        }

        public double FallOff
        {
            get { return (double) GetValue(FallOffProperty); }
            set { SetValue(FallOffProperty, value); }
        }

        public double MaxDistance
        {
            get { return (double) GetValue(MaxDistanceProperty); }
            set { SetValue(MaxDistanceProperty, value); }
        }

        protected override void OnAttached()
        {
            base.OnAttached();

            Dispatcher.BeginInvoke(delegate
                                       {
                                           entity = Entity.GetElementEntity(AssociatedObject);

                                           if (entity != null)
                                           {
                                               worldMagnets.Add(this);

                                               CompositionTarget.Rendering += HandleTick;
                                           }
                                       });
        }

        protected override void OnDetaching()
        {
            base.OnDetaching();


            if (entity != null)
            {
                CompositionTarget.Rendering -= HandleTick;

                worldMagnets.Remove(this);
            }
        }

        private void HandleTick(object sender, EventArgs e)
        {
            foreach (var other in worldMagnets)
            {
                if (other != this)
                {
                    Vector force = other.entity.BodyPosition - entity.BodyPosition;

                    if (force.Length < (MaxDistance + other.MaxDistance))
                    {
                        force = 5000*force*(1/Math.Pow(force.LengthSquared, FallOff + other.FallOff))*
                                (Magnetism + other.Magnetism);

                        entity.Body.ApplyForce(force, other.entity.BodyPosition);
                    }
                }
            }
        }
    }
}